/**
 * Copyright (c) 2015, 漆夜
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include "redis_wrapper.h"

void show_reply(const redisReply *redis_reply)
{
    switch (redis_reply->type) {
    case REDIS_REPLY_INTEGER:
        std::cout << "integer " <<redis_reply->integer << std::endl;
        break;

    case REDIS_REPLY_STRING:
        std::cout << "char[" << redis_reply->len << "]: " << redis_reply->str << std::endl;
        break;

    case REDIS_REPLY_ARRAY:
        for (size_t var = 0; var < redis_reply->elements; ++var)
            show_reply(redis_reply->element[var]);
        break;

    default:
        std::cout << "unknow type" << std::endl;
        break;
    }
}

bool redis::ok(redisContext *redis_handle) {
    if (NULL == redis_handle)
        return false;

    return (redis_handle->err == 0);

}

const char *redis::error_str(redisContext *redis_handle) {
    if (NULL == redis_handle)
        return "";

    return redis_handle->errstr;
}

bool redis::reply::is_string(const redisReply *reply) {
    if (NULL == reply){
        return false;
    }

    return (REDIS_REPLY_STRING == reply->type);
}

bool redis::reply::is_integer(const redisReply *reply) {
    if (NULL == reply){
        return false;
    }

    return (REDIS_REPLY_INTEGER == reply->type);
}

bool redis::reply::is_array(const redisReply *reply) {
    if (NULL == reply){
        return false;
    }

    return (REDIS_REPLY_ARRAY == reply->type);
}

bool redis::reply::is_nil(const redisReply *reply) {
    if (NULL == reply) {
        return false;
    }

    return (REDIS_REPLY_NIL == reply->type);
}

bool redis::reply::is_status(const redisReply *reply) {
    if (NULL == reply) {
        return false;
    }

    return (REDIS_REPLY_STATUS == reply->type);
}

bool redis::reply::is_error(const redisReply *reply) {
    if (NULL == reply) {
        return false;
    }

    return (REDIS_REPLY_ERROR == reply->type);
}

redis::wrapper::RedisReply::RedisReply() :
    m_reply(NULL), m_is_share(0), m_is_null(0), m_is_msgpack_error(0)
{

}

redis::wrapper::RedisReply::RedisReply(redisReply *reply) :
    m_reply(reply), m_is_share(0), m_is_null(0), m_is_msgpack_error(0)
{

}

redis::wrapper::RedisReply::~RedisReply() {
    if (NULL != m_reply) {
        freeReplyObject(m_reply);
    }
}

redis::wrapper::RedisReply &redis::wrapper::RedisReply::operator =(redisReply *reply) {
    this->m_reply = reply;
    return *this;
}

bool redis::wrapper::RedisReply::is_vaild() const {
    return NULL != m_reply;
}

std::string redis::wrapper::RedisReply::get_string() const {
    if (redis::reply::is_string(m_reply)) {
        return std::string(m_reply->str, m_reply->len);
    }
    return "";
}

long long redis::wrapper::RedisReply::get_integer() const {
    if (!redis::reply::is_integer(m_reply))
        return 0;
    return m_reply->integer;
}

bool redis::wrapper::RedisReply::is_msgpack_error() const
{
    return 1 == m_is_msgpack_error;
}

std::vector<std::string> redis::wrapper::RedisReply::get_string_array() const {
    std::vector<std::string> string_array;
    if (!redis::reply::is_array(m_reply))
        return string_array;

    redisReply *reply = m_reply;
    string_array.reserve(reply->elements);
    for (size_t var = 0; var < reply->elements; ++var) {
        if (redis::reply::is_string(reply->element[var])) {
            std::string v(reply->element[var]->str, reply->element[var]->len);
            string_array.push_back(v);
        }
    }
    return string_array;
}

std::string redis::wrapper::RedisReply::get_error() const {
    std::string error_str;
    if (redis::reply::is_error(m_reply)) {
        error_str = m_reply->str;
    }

    return "ok";
}

redisReply *redis::wrapper::RedisReply::c_data() const {
    return m_reply;
}

redis::wrapper::RedisConnecter::RedisConnecter():
    m_ip("127.0.0.1"),
    m_port(6379),
    m_is_shared(false),
    m_auto_disconnect(true),
    m_is_msgpack_error(false),
    m_redis_context(0)
{}

redis::wrapper::RedisConnecter::RedisConnecter(const char *ip, int32_t port, bool is_shared, bool auto_disconnect):
    m_ip(ip),
    m_port(port),
    m_is_shared(is_shared),
    m_auto_disconnect(auto_disconnect),
    m_is_msgpack_error(false),
    m_redis_context(0)
{ }

redis::wrapper::RedisConnecter::~RedisConnecter() {
    if (m_auto_disconnect) {
        disconnect();
    }
}

bool redis::wrapper::RedisConnecter::connect() {
    if (NULL != m_redis_context) {
        redisFree(m_redis_context);
        m_redis_context = NULL;
    }

    m_redis_context = redisConnect(m_ip.c_str(), m_port);
    m_is_shared = false;

    return redis::ok(m_redis_context);
}

bool redis::wrapper::RedisConnecter::connect(const char *ip, int32_t port, bool is_shared, bool auto_disconnect) {

    m_ip = ip;
    m_port = port;
    m_is_shared = is_shared;
    m_auto_disconnect = auto_disconnect;

    return connect();
}

void redis::wrapper::RedisConnecter::disconnect() {
    if (NULL != m_redis_context) {
        redisFree(m_redis_context);
        m_redis_context = NULL;
    }
}

bool redis::wrapper::RedisConnecter::ok() {
    return redis::ok(m_redis_context);
}

const char *redis::wrapper::RedisConnecter::error_str() {
    return redis::error_str(m_redis_context);
}

void redis::wrapper::RedisConnecter::msgpack_error(bool is)
{
    m_is_msgpack_error = is;
}

bool redis::wrapper::RedisConnecter::msgpack_error()
{
    return m_is_msgpack_error;
}

redisContext *redis::wrapper::RedisConnecter::get_context() {
    return m_redis_context;
}

bool redis::wrapper::RedisConnecter::cmd(const char *redis_cmd, ...) {

    va_list args;
    va_start(args, redis_cmd);
    redis::wrapper::RedisReply reply ((redisReply *) redisvCommand(m_redis_context, redis_cmd, args));
    va_end(args);
    if (!redis::ok(m_redis_context)) {
        return false;
    }

    if (!reply.is_vaild()) {
        return false;

    }

    show_reply(reply.c_data());

    return true;
}

redisReply *redis::wrapper::RedisCmder::run_cmd_c(redis::wrapper::RedisConnecter *connecter, const char *redis_cmd, ...)
{
    va_list args;
    va_start(args, redis_cmd);
    redisReply *reply = (redisReply *) redisvCommand(connecter->get_context(), redis_cmd, args);
    va_end(args);

    return reply;
}
